package lion.framework.core.db;

import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import lion.dev.exception.DBException;
import lion.dev.lang.MapJ;
import lion.framework.core.bean.BeanFactory;
import lion.framework.core.conf.Config;
import lion.framework.core.conf.ConfigManager;
import lion.framework.core.db.datasource.IDataSourceProvider;

/**
 * 数据源管理类
 *
 * <pre>
 * time: 2012-11-23 下午04:40:11
 * mail: hl_0074@sina.com
 * </pre>
 *
 * @author lion
 */
public class DataSourceManager {

	private static final Map<String, String> dburlTemplateMap = new HashMap<>();

	private static final Map<String, String> dbDriverMap = new HashMap<>();

	private static Map<String, Class<IDataSourceProvider>> providers = new HashMap<>();

	private static boolean inited = false;

	private static MapJ dataSourceMap = new MapJ();

	private static MapJ dataSourceConfigs = new MapJ();

	public static void addDataSource(String name, MapJ dataSource) {

		dataSourceConfigs.put(name, dataSource);
	}

	public static Connection getConnection() {

		return getConnection("default");
	}

	public static Connection getConnection(String name) {

		try {
			return getDataSource(name).getConnection();
		} catch (Exception e) {
			throw new DBException(e.getMessage(), e);
		}
	}

	public static DataSource getDataSource() {

		return getDataSource("default");
	}

	public static DataSource getDataSource(String name) {

		if (!inited) {
			initConfig();
		}

		DataSource ds = dataSourceMap.getE(name);
		if (ds == null) {
			if (!dataSourceConfigs.containsKey(
					name)) { throw new DBException(String.format("datasource [%s] was not configured!", name)); }

			try {
				ds = initDataSource(name);
			} catch (Exception e) {
				throw new DBException(String.format("init datasource [%s] error", name), e);
			}
			dataSourceMap.put(name, ds);
		}
		return ds;
	}

	public static String getDriverURLTemplate(String dbtype) {

		return dburlTemplateMap.get(dbtype.toLowerCase());
	}

	public static String getJavaTypeName(String colType) {

		if (colType.indexOf("char") > -1 || colType.indexOf("text") > -1
				|| colType.indexOf("long") > -1) { return "String"; }
		if (colType.indexOf("date") > -1 || colType.indexOf("time") > -1) { return "Date"; }
		if (colType.indexOf("bool") > -1 || colType.indexOf("bit") > -1
				|| colType.indexOf("tinyint") > -1) { return "boolean"; }
		if (colType.indexOf("dec") > -1 || colType.indexOf("fixed") > -1
				|| colType.indexOf("numeric") > -1) { return "double"; }
		if (colType.indexOf("bigint") > -1) { return "long"; }
		if (colType.indexOf("float") > -1) { return "float"; }
		if (colType.indexOf("double") > -1) { return "double"; }
		if (colType.indexOf("smallint") > -1) { return "short"; }
		if (colType.indexOf("int") > -1 || colType.indexOf("set") > -1 || colType.indexOf("year") > -1
				|| colType.indexOf("enum") > -1) { return "int"; }
		if (colType.indexOf("lob") > -1 || colType.indexOf("raw") > -1 || colType.indexOf("file") > -1
				|| colType.indexOf("binary") > -1) { return "byte[]"; }

		return colType;

	}

	public static String getJDBCDriver(String dbtype) {

		return dbDriverMap.get(dbtype);
	}

	public static String getSourceType(String name) {

		if (!inited) {
			initConfig();
		}

		if (!dataSourceConfigs.containsKey(name)) { return ""; }
		return ((MapJ) dataSourceConfigs.getE(name)).getString("type");
	}

	public static MapJ dataSourceConfigs() {

		MapJ mapJ = new MapJ();
		mapJ.putAll(dataSourceConfigs);

		return mapJ;
	}

	public static void registProvider(String name, Class<IDataSourceProvider> clazz) {

		providers.put(name, clazz);
	}

	private static Config initConfig() {

		Config config = ConfigManager.getConfig("framework");
		List<MapJ> datasources = config.getList("framework.datasources.datasource");
		for (MapJ datasource : datasources) {
			dataSourceConfigs.put(datasource.getString("name"), datasource);
		}
		inited = true;
		return config;
	}

	/**
	 * init datasource
	 */
	private static DataSource initDataSource(String name) throws Exception {

		MapJ mapJ = dataSourceConfigs.getE(name);

		String providerName = mapJ.getString("provider");
		if (providerName == null) { throw new DBException(
				String.format("provider for [%s] not found configured!", name)); }

		Class<IDataSourceProvider> clazz = providers.get(providerName);
		if (clazz == null) { throw new DBException(
				String.format("the provider [%s] class for datasource [%s] was not not found!", providerName, name)); }
		IDataSourceProvider provider = BeanFactory.create(clazz, mapJ);

		return provider.getDataSource();
	}

	static {
		// drivers
		dbDriverMap.put("mysql", "com.mysql.jdbc.Driver");
		dbDriverMap.put("oracle", "oracle.jdbc.driver.OracleDriver");
		dbDriverMap.put("sqlserver2000", "com.microsoft.jdbc.sqlserver.SQLServerDriver");
		dbDriverMap.put("sqlserver2005", "com.microsoft.sqlserver.jdbc.SQLServerDriver");
		dbDriverMap.put("pgsql", "org.postgresql.Driver");
		dbDriverMap.put("db2", "com.ibm.db2.jdbc.app.DB2Driver");
		dbDriverMap.put("sybase", "com.sybase.jdbc3.jdbc.SybDriver");
		// dburls
		dburlTemplateMap.put("mysql", "jdbc:mysql://%s:%d/%s?useUnicode=true&zeroDateTimeBehavior=convertToNull");
		dburlTemplateMap.put("oracle", "jdbc:oracle:thin:@%s:%d:%s");
		dburlTemplateMap.put("sqlserver2000", "jdbc:microsoft:sqlserver://%s:%d; DatabaseName=%s");
		dburlTemplateMap.put("sqlserver2005", "jdbc:sqlserver://%s:%d; DatabaseName=%s");
		dburlTemplateMap.put("pgsql", "jdbc:postgresql://%s:%d/%s");
		dburlTemplateMap.put("db2", "jdbc:db2://%s:%d/%s");
		dburlTemplateMap.put("sybase", "jdbc:sybase:Tds:%s:%d/%s");
	}
}
